package com.jingdianjichi.practice.server.service.Impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.google.common.base.Preconditions;
import com.jingdianjichi.practice.api.common.Result;
import com.jingdianjichi.practice.api.enums.CompleteStatusEnum;
import com.jingdianjichi.practice.api.enums.IsDeletedFlagEnum;
import com.jingdianjichi.practice.api.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.practice.api.req.*;
import com.jingdianjichi.practice.api.vo.*;
import com.jingdianjichi.practice.server.entity.dto.PracticeSubjectDTO;
import com.jingdianjichi.practice.server.entity.dto.SubjectDTO;
import com.jingdianjichi.practice.server.entity.dto.SubjectDetailDTO;
import com.jingdianjichi.practice.server.entity.dto.SubjectOptionDTO;
import com.jingdianjichi.practice.server.entity.po.*;
import com.jingdianjichi.practice.server.mapper.*;
import com.jingdianjichi.practice.server.rpc.UserRpc;
import com.jingdianjichi.practice.server.rpc.entity.UserInfo;
import com.jingdianjichi.practice.server.utils.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.mapstruct.ap.shaded.freemarker.template.utility.DateUtil;
import org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.lang.model.element.VariableElement;
import javax.security.auth.Subject;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName:PracticeDetailServiceImpl
 * Package:com.jingdianjichi.practice.server.service.Impl
 * Description:
 *
 * @Author fst
 * @Create 2025/1/21 11:38
 * @Version 1.0
 */

@Service
@Slf4j
public class PracticeDetailServiceImpl implements PracticeDetailService
{

    @Resource
    private PracticeDetailDao practiceDetailDao;

    @Resource
    private PracticeDao practiceDao;

    @Resource
    private PracticeSetDao practiceSetDao;

    @Resource
    private SubjectRadioDao subjectRadioDao;

    @Resource
    private SubjectMultipleDao subjectMultipleDao;

    @Resource
    private SubjectJudgeDao subjectJudgeDao;

    @Resource
    private SubjectDao subjectDao;

    @Resource
    private SubjectMappingDao subjectMappingDao;

    @Resource
    private SubjectLabelDao subjectLabelDao;

    @Resource
    private UserRpc userRpc;



    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Boolean> submit(SubmitPracticeDetailReq req) {
        try {
            if(log.isInfoEnabled())
            {
                log.info("PracticeDetailServiceImpl.submit:{}",req);
            }

            PracticePO practicePO = new PracticePO();
            Long practiceId = req.getPracticeId();
            Long setId = req.getSetId();
            practicePO.setId(setId);

            String timeUse= req.getTimeUse();
            String hour = timeUse.substring(0, 2);
            String minute = timeUse.substring(2, 4);
            String second = timeUse.substring(4, 6);

            practicePO.setTimeUse(hour+":"+minute+":"+second);
            practicePO.setSubmitTime(DateUtils.parseDate(req.getSubmitTime(), "yyyy-MM-dd HH:mm:ss"));
            practicePO.setCompleteStatus(CompleteStatusEnum.COMPLETE.getCode());
            practicePO.setCreatedBy(LoginUtil.getLoginId());
            practicePO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
            practicePO.setCreatedTime(new Date());


            //计算正确率
            Integer correctCount=practiceDetailDao.selectCorretCount(practiceId);
            List<PracticeSetDetailPO> practiceSetDetailPOList=practiceDetailDao.selectBySetId(setId);
            Integer totalCount=practiceSetDetailPOList.size();

            BigDecimal correctRate = new BigDecimal(correctCount).
                    divide(new BigDecimal(totalCount),4,BigDecimal.ROUND_HALF_UP).
                    multiply(new BigDecimal(100.00));

            practicePO.setCorrectRate(correctRate);

            PracticePO po =practiceDao.selectById(practiceId);

            if(Objects.nonNull(po))
            {
                practiceDao.insert(po);
            }
            else
            {
                practicePO.setId(practiceId);
                practiceDao.update(practicePO);
            }

            practiceSetDao.updateHeat(setId);

            List<PracticeDetailPO> practiceDetailPOList=practiceDetailDao.selectByPracticeId(practiceId);

            List<PracticeSetDetailPO> minusList = practiceSetDetailPOList.stream().filter(item-> !practiceDetailPOList.stream().
                    map(e ->e.getPracticeId()).
                    collect(Collectors.toList()).
                    contains(item.getSubjectId())).collect(Collectors.toList());

            if(Objects.nonNull(minusList) && !minusList.isEmpty())
            {
                minusList.forEach(item->{
                    PracticeDetailPO practiceDetailPO=new PracticeDetailPO();

                    practiceDetailPO.setPracticeId(practiceId);
                    practiceDetailPO.setSubjectId(item.getSubjectId());
                    practiceDetailPO.setSubjectType(item.getSubjectType());
                    practiceDetailPO.setAnswerStatus(0);
                    practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());
                    practiceDetailPO.setCreatedTime(new Date());
                    practiceDetailPO.setAnswerContent("");

                    practiceDetailDao.insert(practiceDetailPO);
                });
            }

            return Result.ok(true);

        }catch (Exception e)
        {
            log.info("PracticeDetailServiceImpl.submit.error:{}",e.getMessage(),e);
            return Result.fail(e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitSubject(SubmitSubjectDetailReq submitSubjectDetailReq) {
        try {

            if(log.isInfoEnabled())
            {
                log.info("PracticeDetailServiceImpl.SubmitSubject:{}",submitSubjectDetailReq);
            }

            String timeUse= submitSubjectDetailReq.getTimeUse();

            if(timeUse.equals("0"))
            {
                timeUse ="000000";
            }

            String hour = timeUse.substring(0, 2);
            String minute = timeUse.substring(2, 4);
            String second = timeUse.substring(4, 6);
            timeUse=hour+":"+minute+":"+second;

            PracticePO practicePO = new PracticePO();

            practicePO.setId(submitSubjectDetailReq.getPracticeId());
            practicePO.setTimeUse(timeUse);
            practicePO.setSubmitTime(new Date());
            practiceDao.update(practicePO);


           PracticeDetailPO  PO =new PracticeDetailPO();

           PO.setPracticeId(submitSubjectDetailReq.getPracticeId());
           PO.setSubjectId(submitSubjectDetailReq.getSubjectId());
           PO.setSubjectType(submitSubjectDetailReq.getSubjectType());

           String answerContent="";

           if(CollectionUtils.isNotEmpty(submitSubjectDetailReq.getAnswerContents()))
           {
               List<Integer> answerContents = submitSubjectDetailReq.getAnswerContents();

               Collections.sort(answerContents);

               //连接逗号
               answerContent = String.join(",", answerContents.stream().map(String::valueOf).collect(Collectors.toList()));
           }

           PO.setAnswerContent(answerContent);

            SubjectDTO subjectDTO = new SubjectDTO();

            subjectDTO.setSubjectId(submitSubjectDetailReq.getSubjectId());
            subjectDTO.setSubjectType(submitSubjectDetailReq.getSubjectType());

            SubjectDetailDTO subjectDetailDTO = getSubjectDetail(subjectDTO);

            StringBuffer correctAnswer =new StringBuffer();

            if(submitSubjectDetailReq.getSubjectType()== SubjectInfoTypeEnum.JUDGE.getCode())
            {
                Integer isCorrect = subjectDetailDTO.getIsCorrect();
                correctAnswer.append(isCorrect);
            }
            else {
                subjectDetailDTO.getOptionList().forEach(e->
                {
                    if(Objects.equals(e.getIsCorrect(),1))
                    {
                        correctAnswer.append(e.getOptionType()).append(",");
                    }
                });
                if(correctAnswer.length()>0)
                {
                    correctAnswer.deleteCharAt(correctAnswer.length()-1);
                }
            }

            if(Objects.equals(correctAnswer.toString(),answerContent)) {
                PO.setAnswerStatus(1);
            }
            else{
                PO.setAnswerStatus(0);
            }

            PO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
            PO.setCreatedBy(LoginUtil.getLoginId());
            PO.setCreatedTime(new Date());

            PracticeDetailPO existDetail =practiceDetailDao.selectDetail(submitSubjectDetailReq.getPracticeId(),
                    submitSubjectDetailReq.getSubjectId(),LoginUtil.getLoginId());

            if(Objects.isNull(existDetail))
            {
                practiceDetailDao.insert(PO);
            }
            else {
                PO.setId(existDetail.getId());
                practiceDetailDao.update(PO);
            }
        }
        catch (Exception e)
        {
            log.info("PracticeDetailServiceImpl.submitSubject.error:{}",e.getMessage(),e);
        }
    }

    @Override
    public List<ScoreDetailVO> getScoreDetail(GetScoreDetailReq req)
    {
        Long practiceId = req.getPracticeId();
        List<ScoreDetailVO> list=new LinkedList<>();

        List<PracticeDetailPO> practiceDetailPOList =practiceDetailDao.selectByPracticeId(practiceId);

        if(CollectionUtils.isEmpty(practiceDetailPOList))
        {
            return list;
        }

        practiceDetailPOList.forEach(e->
        {
            ScoreDetailVO scoreDetailVO=new ScoreDetailVO();
            scoreDetailVO.setSubjectId(e.getSubjectId());
            scoreDetailVO.setSubjectType(e.getSubjectType());
            scoreDetailVO.setIsCorrect(e.getAnswerStatus());
            list.add(scoreDetailVO);
        });
        return list;
    }

    @Override
    public SubjectDetailVO getSubjectDetail(GetSubjectDetailReq req) {
        SubjectDetailVO subjectDetailVO = new SubjectDetailVO();

        Long subjectId =req.getSubjectId();

        Integer subjectType =req.getSubjectType();

        SubjectDTO subjectDTO =new SubjectDTO();

        subjectDTO.setSubjectId(subjectId);

        subjectDTO.setSubjectType(subjectType);

        SubjectDetailDTO subjectDetailDTO = getSubjectDetail(subjectDTO);

        List<SubjectOptionDTO> optionList = subjectDetailDTO.getOptionList();

        List<PracticeSubjectOptionVO> optionVOList =new LinkedList<>();

        List<Integer> correctAnswer =new LinkedList<>();

        if(CollectionUtils.isNotEmpty(optionList))
        {
            optionList.forEach(option->{
                PracticeSubjectOptionVO optionVO =new PracticeSubjectOptionVO();
                optionVO.setOptionType(option.getOptionType());
                optionVO.setOptionContent(option.getOptionContent());
                optionVO.setIsCorrect(option.getIsCorrect());
                optionVOList.add(optionVO);

                if(Objects.equals(option.getIsCorrect(),1))
                {
                    correctAnswer.add(option.getOptionType());
                }
            });
        }

        if(subjectType.equals(SubjectInfoTypeEnum.JUDGE.getCode()))
        {
            Integer isCorrect = subjectDetailDTO.getIsCorrect();

            PracticeSubjectOptionVO correctOption =new PracticeSubjectOptionVO();

            correctOption.setOptionType(1);
            correctOption.setOptionContent("正确");
            correctOption.setIsCorrect(isCorrect==1?1:0);;

            PracticeSubjectOptionVO errorOption =new PracticeSubjectOptionVO();

            errorOption.setOptionType(2);
            errorOption.setOptionContent("错误");
            errorOption.setIsCorrect(isCorrect==1?0:1);

            optionVOList.add(correctOption);
            optionVOList.add(errorOption);
            correctAnswer.add(isCorrect);
        }
        subjectDetailVO.setSubjectName(subjectDetailDTO.getSubjectName());
        subjectDetailVO.setSubjectParse(subjectDetailDTO.getSubjectParse());
        subjectDetailVO.setOptionList(optionVOList);
        subjectDetailVO.setCorrectAnswer(correctAnswer);

        //填入自己的答案
        List<Integer> responderAnswer =new LinkedList<>();
        PracticeDetailPO practiceDetailPO =practiceDetailDao.selectAnswer(req.getPracticeId(),subjectId);

        String answerContent =practiceDetailPO.getAnswerContent();

        if(StringUtils.isNotBlank(answerContent))
        {
            String [] split=answerContent.split(",");

            for(String str:split)
            {
                responderAnswer.add(Integer.parseInt(str));
            }
        }

        subjectDetailVO.setRespondAnswer(responderAnswer);

        List<SubjectMappingPO> subjectMappingPOList =subjectMappingDao.selectBySubjectId(subjectId);
        List<Long> labelIdList =new LinkedList<>();
        subjectMappingPOList.forEach(e->
        {
            labelIdList.add(e.getLabelId());
        });

        List<String> labelNameList = subjectLabelDao.getLabelNameByIds(labelIdList);
        subjectDetailVO.setLabelNames(labelNameList);
        return subjectDetailVO;
    }

    @Override
    public ReportVO getReport(GetReportReq req) {
        ReportVO reportVO=new ReportVO();
        Long practiceId =req.getPracticeId();

        PracticePO practicePO =practiceDao.selectById(practiceId);

        Long setId =practicePO.getSetId();

        PracticeSetPO practiceSetPO =practiceSetDao.selectById(setId);

        reportVO.setTitle(practiceSetPO.getSetName());

        List<PracticeDetailPO> practiceDetailPOList =practiceDetailDao.selectByPracticeId(practiceId);

        if(CollectionUtils.isEmpty(practiceDetailPOList))
        {
            return null;
        }

        int totalCount=practiceDetailPOList.size();

        List<PracticeDetailPO> correctPoList =practiceDetailPOList.stream().filter(e->
           Objects.equals(e.getAnswerStatus(),1)).collect(Collectors.toList());

        int correctCount=correctPoList.size();

        reportVO.setCorrectSubject(correctCount+"/"+totalCount);

        List<ReportSkillVO> reportSkillVOS =new LinkedList<>();

        Map<Long,Integer> totalMap = getSubjectLabelMap(practiceDetailPOList);

        Map<Long,Integer> correctMap=getSubjectLabelMap(correctPoList);

        totalMap.forEach((key,value)->
        {
            ReportSkillVO reportSkillVO=new ReportSkillVO();

            SubjectLabelPO subjectLabelPO =subjectLabelDao.queryById(key);

            String labelName =subjectLabelPO.getLabelName();

            Integer correctCnt =correctMap.get(key);

            if(Objects.isNull(correctCnt))
            {
                correctCnt=0;
            }

            reportSkillVO.setName(labelName);
            BigDecimal rate = BigDecimal.ZERO;

            if(!Objects.equals(value,0))
            {
                rate =new BigDecimal(correctCnt.toString()).divide(new BigDecimal(value.toString()),4,BigDecimal.ROUND_HALF_UP)
                        .multiply(new BigDecimal(100));
            }

            reportSkillVO.setStar(rate);
            reportSkillVOS.add(reportSkillVO);
        });

        if(log.isInfoEnabled())
        {
            log.info("getReport:{}", JSON.toJSONString(reportSkillVOS));
        }

        reportVO.setSkill(reportSkillVOS);
        return reportVO;
    }

    @Override
    public List<RankVO> getPracticeRankList()
    {
        List<RankVO> list =new LinkedList<>();
        List<PracticeRankPO> poList =practiceDetailDao.getPracticeCount();

        if(CollectionUtils.isEmpty(poList))
        {
            return list;
        }

        poList.forEach(po->
        {
            RankVO rankVO=new RankVO();
            rankVO.setCount(po.getCount());
            UserInfo userInfo =userRpc.getUserInfo(po.getCreatedBy());

            rankVO.setAvatar(userInfo.getAvatar());
            rankVO.setName(userInfo.getNickName());

            list.add(rankVO);
        });

        return list;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void giveUp(Long practiceId)
    {
        practiceDetailDao.deleteByPracticeId(practiceId);
        practiceDao.deleteById(practiceId);
    }

    private Map<Long, Integer> getSubjectLabelMap(List<PracticeDetailPO> practiceDetailPOList)
    {
        if(CollectionUtils.isEmpty(practiceDetailPOList))
        {
            return Collections.emptyMap();
        }

        Map<Long,Integer> map=new HashMap<>();

        practiceDetailPOList.forEach(e->
        {
            Long subjectId =e.getSubjectId();
            List<SubjectMappingPO> labelIdPO=subjectMappingDao.getLabelIdsBySubjectId(subjectId);
            labelIdPO.forEach(po->
            {
                Long labelId=po.getLabelId();
                if(Objects.isNull(map.get(labelId)))
                {
                    map.put(labelId,1);
                    return ;
                }
                map.put(labelId,map.get(labelId)+1);
            });
        });

        if(log.isInfoEnabled())
        {
            log.info("getSubjectLabelMap:{}", JSON.toJSONString(map));
        }

        return map;
    }

    private SubjectDetailDTO getSubjectDetail(SubjectDTO dto)
    {
        SubjectDetailDTO subjectDetailDTO=new SubjectDetailDTO();
        SubjectPO subjectPO=subjectDao.selectById(dto.getSubjectId());

        if(dto.getSubjectType()== SubjectInfoTypeEnum.RADIO.getCode())
        {
            List<SubjectOptionDTO> optionList = new LinkedList<>();
            List<SubjectRadioPO> radioSubjectPOS =subjectRadioDao.selectBySubjectId(dto.getSubjectId());

            radioSubjectPOS.forEach(e->
            {
                SubjectOptionDTO subjectOptionDTO =new SubjectOptionDTO();
                subjectOptionDTO.setOptionContent(e.getOptionContent());
                subjectOptionDTO.setOptionType(e.getOptionType());
                subjectOptionDTO.setIsCorrect(e.getIsCorrect());
                optionList.add(subjectOptionDTO);
            });
            subjectDetailDTO.setOptionList(optionList);
        }
        if(dto.getSubjectType()== SubjectInfoTypeEnum.MULTIPLE.getCode())
        {
            List<SubjectOptionDTO> optionList = new LinkedList<>();
            List<SubjectMultiplePO> multipleSubjectPOS =subjectMultipleDao.selectBySubjectId(dto.getSubjectId());
            multipleSubjectPOS.forEach(e->
            {
                SubjectOptionDTO subjectOptionDTO =new SubjectOptionDTO();
                subjectOptionDTO.setOptionContent(e.getOptionContent());
                subjectOptionDTO.setOptionType(e.getOptionType());
                subjectOptionDTO.setIsCorrect(e.getIsCorrect());
                optionList.add(subjectOptionDTO);
            });
            subjectDetailDTO.setOptionList(optionList);
        }
        if(dto.getSubjectType()==SubjectInfoTypeEnum.JUDGE.getCode())
        {
            SubjectJudgePO subjectJudgePO =subjectJudgeDao.selectBySubjectId(dto.getSubjectId());
            subjectDetailDTO.setIsCorrect(subjectJudgePO.getIsCorrect());
        }

        subjectDetailDTO.setSubjectParse(subjectPO.getSubjectParse());
        subjectDetailDTO.setSubjectName(subjectPO.getSubjectName());
        return subjectDetailDTO;
    }
}
